#include <stdlib.h>
#include <stdio.h>

#include <unistd.h>

#define private static
#define public

#define FILE_NAME "work.txt"
#define INPUT_FILE_NAME FILE_NAME
#define OUTPUT_FILE_NAME FILE_NAME

typedef FILE *File;
typedef char* String;

/* 职员对象 */

typedef struct _Employee {
  char name[8];
  int age;
  int salary;
} *Employee;

typedef Employee (*EmployeeFn)(Employee);

private Employee employee_free(Employee employee) {
  free(employee);
  return NULL;
}

private Employee employee_read(Employee employee) {
  employee = (Employee) calloc(1, sizeof(struct _Employee));
  if (employee == NULL) {
    fprintf(stderr, "employee_read: out of memory\n");
    exit(EXIT_FAILURE);
  }
  if (scanf("%s%d%d", employee->name, &employee->age, &employee->salary) != 3) {
    employee_free(employee);
    return NULL;
  }
  return employee;
}

private Employee employee_print(Employee employee) {
  printf("%s %d %d\n", employee->name, employee->age, employee->salary);
  return employee;
}

private Employee employee_adjust_salary(Employee employee) {
  if (employee->salary < 30000) {
    employee->salary += 3000;
  }
  return employee;
}

/* 职员列表对象 */

typedef struct _Employees {
  Employee employee;
  struct _Employees *next;
} *Employees;

typedef Employees (*EmployeesFn)(Employees);

private Employees employees_map(Employees employees, EmployeeFn fn) {
  for (Employees p = employees; p; p = p->next) {
    p->employee = fn(p->employee);
  }
  return employees;
}

private Employees employees_read(Employees head) {
  Employees tail = NULL;
  for (;;) {
    Employee employee = employee_read(NULL);
    if (employee == NULL) {
      return head;
    }

    Employees employees = (Employees) calloc(1, sizeof(Employees));
    if (employees == NULL) {
      fprintf(stderr, "employees_read: out of memory\n");
      exit(EXIT_FAILURE);
    }

    if (tail == NULL) {
      head = tail = employees;
    } else {
      tail->next = employees;
      tail = tail->next;
    }
    tail->employee = employee;
  }
}

public Employees employees_print(Employees employees) {
  return employees_map(employees, employee_print);
}

public void employees_adjust_salary(Employees employees) {
  employees_map(employees, employee_adjust_salary);
}

public void employees_free(Employees employees) {
  employees_map(employees, employee_free);
  while (employees) {
    Employees e = employees;
    employees = employees->next;
    free(e);
  }
}

/* I/O层 */

private File file_open(String filename, String mode) {
  File stream = freopen(filename, mode, mode[0] == 'r'? stdin: stdout);
  if (stream == NULL) {
    fprintf(stderr, "Cannot open %s with %s mode.\n", filename, mode);
    exit(EXIT_FAILURE);
  }
  return stream;
}

private Employees file_with(String filename, String mode, Employees employees, EmployeesFn fn) {
  int handler = dup(mode[0] == 'r'? 0: 1); /* 克隆文件句柄 */
  File stream = file_open(filename, mode);
  employees = fn(employees);
  fclose(stream);
  fdopen(handler, mode);                   /* 完成后恢复标准IO */
  return employees;
}

public Employees employees_input(String filename) {
  return file_with(filename, "r", NULL, employees_read);
}

public void employees_output(Employees employees, String filename) {
  file_with(filename, "w", employees, employees_print);
}

/* 应用层 */

int main(void) {
  Employees employees = employees_input(INPUT_FILE_NAME); /* 从文件读入 */
  employees_print(employees);                             /* 1. 输出到屏幕 */
  employees_adjust_salary(employees);                     /* 2. 调整薪资 */
  employees_print(employees);                             /* 3. 输出调整后的结果 */
  employees_output(employees, OUTPUT_FILE_NAME);          /* 4. 保存到文件 */
  employees_free(employees);                              /* 释放资源 */

  return EXIT_SUCCESS;
}
